﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;

using Q.Extensions;

namespace Q.Diagnostics
{
    internal class PerformanceCounterSet
        : Dictionary<PerformanceWatchType, PerformanceCounter>, IDisposable
    {
        public PerformanceCounterSet() { }

        public PerformanceCounterSet(PerformanceWatchType types)
        {
            AddWatch(types);
        }

        public void AddWatch(PerformanceWatchType types)
        {
            Process p = Process.GetCurrentProcess();
            
            types.ForEach((PerformanceWatchType type) =>
            {
                try
                {
                    if (!ContainsKey(type))
                    {
                        switch (type)
                        {
                            case PerformanceWatchType.TotalCpuUsage:
                                {
                                    this.Add(type, new PerformanceCounter(
                                        "Processor",
                                        "% Processor Time",
                                        "_Total"));
                                } break;
                            case PerformanceWatchType.AvailablePhysicalMemroy:
                                {
                                    this.Add(type, new PerformanceCounter(
                                        "Memory",
                                        "Available Bytes"));
                                } break;
                            case PerformanceWatchType.ProcessCpuUsage:
                                {
                                    this.Add(type, new PerformanceCounter(
                                        "Process",
                                        "% Processor Time",
                                        p.ProcessName));
                                } break;
                            case PerformanceWatchType.ProcessWorkingSet:
                                {
                                    this.Add(type, new PerformanceCounter(
                                        "Process",
                                        "Working Set",
                                        p.ProcessName));
                                } break;
                        } 
                    }
                }
                catch (InvalidOperationException)
                {
                    throw new NotSupportedException(
                        "One of the types is invalid on the machine.");
                }
            });
        }

        public int RemoveWatch(PerformanceWatchType types)
        {
            int count = 0;
            types.ForEach((PerformanceWatchType type) =>
            {
                if (ContainsKey(type))
                {
                    var counter = this[type];
                    if (counter != null)
                    {
                        counter.Close();
                    }

                    if (this.Remove(type))
                    {
                        count++;
                    }
                }
            });

            return count;
        }

        public void Dispose()
        {
            Dispose(true);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                foreach (var counter in this.Values)
                {
                    if (counter != null)
                    {
                        counter.Close();
                    }
                }

                Clear();

                GC.SuppressFinalize(this);
            }
        }

        ~PerformanceCounterSet()
        {
            Dispose(false);
        }
    }
}
